using System;
using System.Text;
using System.Globalization;
using System.Security.Cryptography;
using System.IO;

namespace Game.Lib
{
    public class Utils
    {
        public static readonly CultureInfo EnUsCulture = new CultureInfo("en-US");
        public static readonly SHA256CryptoServiceProvider CSP = new SHA256CryptoServiceProvider();
        public static readonly UTF8Encoding UTF8 = new UTF8Encoding();

        public static readonly float PI = (float)Math.PI;
        public static readonly float PI_BY_TWO = (float)(Math.PI / 2.0);
        public static readonly float TWO_PI = (float)(Math.PI * 2.0);
        public static readonly float RAD_TO_DEG = (float)(Math.PI / 180.0);
        public static readonly float DEG_TO_RAD = (float)(180.0 / Math.PI);
        public static readonly float Epsilon = .000001f;

        public static uint PacketCounter = 0;

        public static float Clamp(float t, float min, float max)
        {
            if(t > max) return max;
            else if(t < min) return min;
            return t;
        }

        public static double Clamp(double t, double min, double max)
        {
            if(t > max) return max;
            if(t < min) return min;
            return t;
        }

        public static float Min(float a, float b)
        {
            if(a < b) return a;
            return b;
        }

        public static double Min(double a, double b)
        {
            if(a < b) return a;
            return b;
        }

        public static float Max(float a, float b)
        {
            if(a > b) return a;
            return b;
        }

        public static double Max(double a, double b)
        {
            if(a > b) return a;
            return b;
        }

        public static bool IsFinite(float a)
        {
            return !(float.IsInfinity(a) || float.IsNaN(a));
        }

        public static bool IsFinite(double a)
        {
            return !(double.IsInfinity(a) || double.IsNaN(a));
        }

        public static float Snap(float a, float t)
        {
            return (float)Math.Round(a / t) * t;
        }

        public static double Snap(double a, double t)
        {
            return Math.Round(a / t) * t;
        }

        public static void GetBytes(string a, Stream s)
        {
            byte[] buf = BitConverter.GetBytes(UTF8.GetByteCount(a));
            if(!BitConverter.IsLittleEndian) Array.Reverse(buf);
            s.Write(buf, 0, 4);
            s.Write(UTF8.GetBytes(a), 0, a.Length);
        }

        public static byte[] GetBytes(string a)
        {
            byte[] ret = new byte[4 + UTF8.GetByteCount(a)];
            Buffer.BlockCopy(BitConverter.GetBytes(UTF8.GetByteCount(a)), 0, ret, 0, 4);
            if (!BitConverter.IsLittleEndian) Array.Reverse(ret, 0, 4);
            Buffer.BlockCopy(UTF8.GetBytes(a), 0, ret, 4, UTF8.GetByteCount(a));
            return ret;
        }

        public static string GetString(Stream a)
        {
            byte[] buf = new byte[4];

            int len = a.Read(buf, 0, 4);
            if(len != 4) throw new EndOfStreamException("Reconstruction of string failed");
            if(!BitConverter.IsLittleEndian) Array.Reverse(buf);
            int cnt = BitConverter.ToInt32(buf, 0);

            buf = new byte[cnt];
            len = a.Read(buf, 0, cnt);
            if(len != cnt) throw new EndOfStreamException("Reconstruction of string failed");

            return UTF8.GetString(buf);
        }

        public static string GetString(byte[] data, int pos)
        {
            if(!BitConverter.IsLittleEndian) Array.Reverse(data, pos, 4);
            int cnt = BitConverter.ToInt32(data, pos);
            return UTF8.GetString(data, pos + 4, cnt);
        }

        public static uint GetPacketID()
        {
            try
            {
                PacketCounter++;
            }
            catch
            {
                PacketCounter = 0;
            }
            return PacketCounter;
        }

        public static bool Inside(float p, float min, float max)
        {
            return p >= min && p <= max;
        }
    }
}
